/*
 * Copyright 2020 the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * https://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openrewrite.maven.search;

import org.assertj.core.api.Condition;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.openrewrite.DocumentExample;
import org.openrewrite.Issue;
import org.openrewrite.maven.table.DependenciesInUse;
import org.openrewrite.maven.table.ExplainDependenciesInUse;
import org.openrewrite.test.RewriteTest;

import static org.assertj.core.api.Assertions.assertThat;
import static org.openrewrite.maven.Assertions.pomXml;

class DependencyInsightTest implements RewriteTest {

    @DocumentExample
    @Test
    void findDependency() {
        rewriteRun(
          spec -> spec.recipe(new DependencyInsight("*guava*", "*", "compile", null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                      <groupId>com.google.guava</groupId>
                      <artifactId>guava</artifactId>
                      <version>29.0-jre</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <!--~~(com.google.guava:failureaccess:1.0.1,com.google.guava:guava:29.0-jre,com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava)~~>--><dependency>
                      <groupId>com.google.guava</groupId>
                      <artifactId>guava</artifactId>
                      <version>29.0-jre</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/1418")
    @Test
    void doesNotMatchTestScope() {
        rewriteRun(
          spec -> spec.recipe(new DependencyInsight("*guava*", "*", "compile", null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                      <groupId>com.google.guava</groupId>
                      <artifactId>guava</artifactId>
                      <version>29.0-jre</version>
                      <scope>test</scope>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void findDependencyTransitively() {
        rewriteRun(
          spec -> spec.recipe(new DependencyInsight("*", "*simpleclient*", "compile", null, null)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                      <groupId>io.micrometer</groupId>
                      <artifactId>micrometer-registry-prometheus</artifactId>
                      <version>1.6.3</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <!--~~(io.prometheus:simpleclient:0.9.0,io.prometheus:simpleclient_common:0.9.0)~~>--><dependency>
                      <groupId>io.micrometer</groupId>
                      <artifactId>micrometer-registry-prometheus</artifactId>
                      <version>1.6.3</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    void onlyDirect() {
        rewriteRun(
          spec -> spec.recipe(new DependencyInsight("*", "*simpleclient*", "compile", null, true)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                      <groupId>io.micrometer</groupId>
                      <artifactId>micrometer-registry-prometheus</artifactId>
                      <version>1.6.3</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }


    @Test
    void versionSelector() {
        rewriteRun(
          spec -> spec.recipe(new DependencyInsight("org.openrewrite", "*", "compile", "8.0.0", true)),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                      <groupId>org.openrewrite</groupId>
                      <artifactId>rewrite-java</artifactId>
                      <version>8.0.0</version>
                  </dependency>
                  <dependency>
                      <groupId>org.openrewrite</groupId>
                      <artifactId>rewrite-yaml</artifactId>
                      <version>7.0.0</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <!--~~(org.openrewrite:rewrite-core:8.0.0,org.openrewrite:rewrite-java:8.0.0)~~>--><dependency>
                      <groupId>org.openrewrite</groupId>
                      <artifactId>rewrite-java</artifactId>
                      <version>8.0.0</version>
                  </dependency>
                  <dependency>
                      <groupId>org.openrewrite</groupId>
                      <artifactId>rewrite-yaml</artifactId>
                      <version>7.0.0</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @ParameterizedTest
    @ValueSource(strings = {
      "6.1.5", // exact
      "6.1.1-6.1.15", // hyphenated
      "[6.1.1,6.1.6)", "[6.1.1,6.1.5]", "[6.1.5,6.1.15]", "(6.1.4,6.1.15]", // full range
      "6.1.X", // X range
      "~6.1.0", "~6.1", // tilde range
    })
    void versionPatterns(String versionPattern) {
        rewriteRun(
          recipeSpec -> recipeSpec.recipe(new DependencyInsight("org.springframework", "*", null, versionPattern, null)),
            //language=xml
            pomXml(
              """
                <project>
                  <groupId>com.example</groupId>
                  <artifactId>foo</artifactId>
                  <version>1.0.0</version>
    
                  <dependencies>
                    <dependency>
                      <groupId>org.springframework</groupId>
                      <artifactId>spring-core</artifactId>
                      <version>6.1.5</version>
                    </dependency>
                    <dependency>
                      <groupId>org.springframework</groupId>
                      <artifactId>spring-aop</artifactId>
                      <version>6.2.2</version>
                    </dependency>
                  </dependencies>
                </project>
                """,
              """
                <project>
                  <groupId>com.example</groupId>
                  <artifactId>foo</artifactId>
                  <version>1.0.0</version>
    
                  <dependencies>
                    <!--~~(org.springframework:spring-core:6.1.5,org.springframework:spring-jcl:6.1.5)~~>--><dependency>
                      <groupId>org.springframework</groupId>
                      <artifactId>spring-core</artifactId>
                      <version>6.1.5</version>
                    </dependency>
                    <dependency>
                      <groupId>org.springframework</groupId>
                      <artifactId>spring-aop</artifactId>
                      <version>6.2.2</version>
                    </dependency>
                  </dependencies>
                </project>
                """
            )
        );
    }

    @Issue("https://github.com/openrewrite/rewrite/issues/6151")
    @Test
    void findTwoDependenciesAndTheirDataTableRows() {
        rewriteRun(
          spec -> spec
            .dataTable(DependenciesInUse.Row.class, rows -> assertThat(rows).hasSize(2))
            .recipes(
            new DependencyInsight("*", "guava", "compile", null, null),
            new DependencyInsight("*", "lombok", "compile", null, null)
          ),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <dependency>
                      <groupId>com.google.guava</groupId>
                      <artifactId>guava</artifactId>
                      <version>29.0-jre</version>
                  </dependency>
                  <dependency>
                      <groupId>org.projectlombok</groupId>
                      <artifactId>lombok</artifactId>
                      <version>1.18.42</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencies>
                  <!--~~(com.google.guava:guava:29.0-jre)~~>--><dependency>
                      <groupId>com.google.guava</groupId>
                      <artifactId>guava</artifactId>
                      <version>29.0-jre</version>
                  </dependency>
                  <!--~~(org.projectlombok:lombok:1.18.42)~~>--><dependency>
                      <groupId>org.projectlombok</groupId>
                      <artifactId>lombok</artifactId>
                      <version>1.18.42</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }

    @Test
    @Disabled("Test is logically correct, but the MavenResolutionResult's dependency graph is not")
    void jacksonIsFoundInternally() {
        rewriteRun(
          spec -> spec.recipe(new DependencyInsight("com.fasterxml.jackson.*", "*", null, null, null))
            .dataTable(DependenciesInUse.Row.class, rows -> {
                assertThat(rows).isNotEmpty();
                assertThat(rows).anyMatch(row ->
                  "com.fasterxml.jackson.datatype".equals(row.getGroupId()) &&
                    "jackson-datatype-jsr310".equals(row.getArtifactId()) &&
                    row.getDepth() == 2);
                assertThat(rows).anyMatch(row ->
                  "com.fasterxml.jackson.core".equals(row.getGroupId()) &&
                    "jackson-core".equals(row.getArtifactId()) &&
                    row.getDepth() == 3);
            })
            .dataTable(ExplainDependenciesInUse.Row.class, rows -> {
                assertThat(rows).isNotEmpty();
                assertThat(rows).haveExactly(1, new Condition<>(row ->
                  "com.fasterxml.jackson.core".equals(row.getGroupId()) &&
                    "jackson-core".equals(row.getArtifactId()) &&
                    "compile".equals(row.getScope()) &&
                    """
                      com.fasterxml.jackson.core:jackson-core:2.13.2
                      +--- com.fasterxml.jackson.core:jackson-databind:2.13.2.2
                      |    +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.2
                      |    |    \\--- org.springframework.boot:spring-boot-starter-json:2.6.6
                      |    |         \\--- org.springframework.boot:spring-boot-starter-web:2.6.6
                      |    |              \\--- compile
                      |    +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2
                      |    |    \\--- org.springframework.boot:spring-boot-starter-json:2.6.6 (*)
                      |    +--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.13.2
                      |    |    \\--- org.springframework.boot:spring-boot-starter-json:2.6.6 (*)
                      |    +--- io.pivotal.cfenv:java-cfenv:2.5.0
                      |    |    +--- io.pivotal.cfenv:java-cfenv-boot:2.5.0
                      |    |    |    \\--- compile
                      |    |    \\--- io.pivotal.cfenv:java-cfenv-jdbc:2.5.0
                      |    |         \\--- io.pivotal.cfenv:java-cfenv-boot:2.5.0 (*)
                      |    \\--- org.springframework.boot:spring-boot-starter-json:2.6.6 (*)
                      +--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.2 (*)
                      +--- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2 (*)
                      +--- com.fasterxml.jackson.module:jackson-module-parameter-names:2.13.2 (*)
                      \\--- io.pivotal.cfenv:java-cfenv:2.5.0 (*)
                      """.equals(row.getDependencyGraph()), "jackson-core dependency graph"));
            }),
          pomXml(
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-dependencies</artifactId>
                      <version>2.6.6</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
                <dependencies>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-web</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-actuator</artifactId>
                      <version>2.6.4</version>
                  </dependency>
                  <dependency>
                    <groupId>io.pivotal.cfenv</groupId>
                    <artifactId>java-cfenv-boot</artifactId>
                    <version>2.5.0</version>
                  </dependency>
                </dependencies>
              </project>
              """,
            """
              <project>
                <groupId>com.mycompany.app</groupId>
                <artifactId>my-app</artifactId>
                <version>1</version>
                <dependencyManagement>
                  <dependencies>
                    <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-dependencies</artifactId>
                      <version>2.6.6</version>
                      <type>pom</type>
                      <scope>import</scope>
                    </dependency>
                  </dependencies>
                </dependencyManagement>
                <dependencies>
                  <!--~~(com.fasterxml.jackson.core:jackson-annotations:2.13.2,com.fasterxml.jackson.core:jackson-core:2.13.2,com.fasterxml.jackson.core:jackson-databind:2.13.2.2,com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.2,com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2,com.fasterxml.jackson.module:jackson-module-parameter-names:2.13.2)~~>--><dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-web</artifactId>
                  </dependency>
                  <!--~~(com.fasterxml.jackson.core:jackson-annotations:2.13.2,com.fasterxml.jackson.core:jackson-core:2.13.2,com.fasterxml.jackson.core:jackson-databind:2.13.2.2,com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.2)~~>--><dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-actuator</artifactId>
                      <version>2.6.4</version>
                  </dependency>
                  <!--~~(com.fasterxml.jackson.core:jackson-annotations:2.13.2,com.fasterxml.jackson.core:jackson-core:2.13.2,com.fasterxml.jackson.core:jackson-databind:2.13.2.2)~~>--><dependency>
                    <groupId>io.pivotal.cfenv</groupId>
                    <artifactId>java-cfenv-boot</artifactId>
                    <version>2.5.0</version>
                  </dependency>
                </dependencies>
              </project>
              """
          )
        );
    }
}
